home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Applications / Eudora 1.3.1 / source / fileutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-16  |  19.4 KB  |  720 lines  |  [TEXT/MPS ]

  1. #define FILE_NUM 13
  2. /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
  3. /**********************************************************************
  4.  * various useful functions related to the filesystem
  5.  **********************************************************************/
  6. #pragma load EUDORA_LOAD
  7. #pragma segment Lib
  8. #define FILL(pb,name,vRef,dirId) do {                                     \
  9.     (pb).ioNamePtr = name;                                                                \
  10.     (pb).ioVRefNum = vRef;                                                                \
  11.     (pb).ioDirID = dirId;                                                                 \
  12.     } while (0)
  13.  
  14. /**********************************************************************
  15.  * GetMyVR - get a volume ref number
  16.  **********************************************************************/
  17. short GetMyVR(UPtr name)
  18. {
  19.     HVolumeParam vInfo;
  20.     
  21.     FILL(*((HFileInfo*)&vInfo),name,0,0);
  22.     vInfo.ioVolIndex = -1;
  23.     if (PBHGetVInfo(&vInfo,FALSE))
  24.         return(0);
  25.     return(vInfo.ioVRefNum);
  26. }
  27.  
  28. /**********************************************************************
  29.  * get a dir ID, given a vrefnum
  30.  **********************************************************************/
  31. long GetMyDirID(short refNum)
  32. {
  33.     WDPBRec wdBlock;        /* to turn a working dir id into a dir id */
  34.     Byte name[32];            /* we gotta give it some room to write the name */
  35.     
  36.     /*
  37.      * HFS.  get working directory info
  38.      */
  39.     wdBlock.ioVRefNum = wdBlock.ioWDVRefNum = refNum;
  40.     wdBlock.ioWDIndex = 0;
  41.     wdBlock.ioWDProcID = nil;
  42.     wdBlock.ioNamePtr = name;
  43.     if (PBGetWDInfo(&wdBlock,0)!=noErr) /* get working directory info */
  44.         return(0);
  45.     else
  46.         return(wdBlock.ioWDDirID);
  47. }
  48.  
  49. /**********************************************************************
  50.  * get a name, given a vRefNum
  51.  **********************************************************************/
  52. short GetDirName(UPtr volName,short vRef, long dirId,UPtr name)
  53. {
  54.     CInfoPBRec catBlock;    /* to get the name of a directory */
  55.     short err;
  56.     short oldVol;
  57.     
  58.     GetVol(nil,&oldVol);
  59.     if (err=SetVol(volName,vRef)) return(err);
  60.     GetVol(nil,&vRef);
  61.     
  62.     *name = 0;
  63.     
  64.     /*
  65.      * get name of directory
  66.      */
  67.     catBlock.dirInfo.ioDrDirID = dirId;
  68.     catBlock.dirInfo.ioNamePtr = name;
  69.     catBlock.dirInfo.ioVRefNum = vRef;
  70.     catBlock.dirInfo.ioFDirIndex = -1;        /* use ioDirID */
  71.     if ((err=PBGetCatInfo(&catBlock,0))!=noErr) /* get working directory info */
  72.         *name = 0;
  73.     SetVol(nil,oldVol);
  74.     return(err);
  75. }
  76.  
  77. /**********************************************************************
  78.  * get a volume name, given a vRefNum
  79.  **********************************************************************/
  80. UPtr GetMyVolName(short refNum,UPtr name)
  81. {
  82.     VolumeParam myParam;
  83.         
  84.     *name = 0;        /* empty string */
  85.     
  86.     myParam.ioNamePtr = name;
  87.     myParam.ioVRefNum = refNum;
  88.     myParam.ioVolIndex = 0;                 /* use refnum only */
  89.     PBGetVInfo(&myParam,0);                 /* get the volume info */
  90.  
  91.     return(name);
  92. }
  93.  
  94. /**********************************************************************
  95.  * find the DirID of the blessed (system) folder
  96.  **********************************************************************/
  97. int BlessedDirID(long *sysDirIDPtr)
  98. {
  99.     HVolumeParam myPB;
  100.     SysEnvRec env;
  101.     int err;
  102.  
  103.     /*
  104.      * does HFS exist?
  105.      */
  106.     if (*(short *)FSFCBLen <= 0) return(-1);
  107.     
  108.     /*
  109.      * find the blessed dir
  110.      */
  111.     SysEnvirons(ENVIRONS_VERSION,&env);
  112.     myPB.ioNamePtr = nil;
  113.     myPB.ioVRefNum = env.sysVRefNum;
  114.     myPB.ioVolIndex = 0;
  115.     if (err=PBHGetVInfo(&myPB,False)) return(err);
  116.     if (myPB.ioVSigWord == 0xd2d7) return(myPB.ioVSigWord); /* MFS */
  117.     *sysDirIDPtr = myPB.ioVFndrInfo[1];
  118.     return(0);
  119. }
  120.  
  121. /**********************************************************************
  122.  * MakeResFile - create a resource file in a given directory
  123.  **********************************************************************/
  124. int MakeResFile(UPtr name,int vRef,long dirId,long creator,long type)
  125. {
  126.     int err;
  127.     
  128.     (void) HCreate(vRef,dirId,name,creator,type);
  129.     (void) HSetVol(nil,vRef,dirId);
  130.     CreateResFile(name);
  131.     err=ResError();
  132.     return (err=dupFNErr ? noErr : err);
  133. }
  134.  
  135. /**********************************************************************
  136.  * turn a DirID into a working directory reference number
  137.  **********************************************************************/
  138. short GetMyWD(short vRef,long dirID)
  139. {
  140.     WDPBRec myPB;
  141.     short err;
  142.     
  143.     myPB.ioNamePtr = nil;
  144.     myPB.ioVRefNum = vRef;
  145.     myPB.ioWDProcID = nil;
  146.     myPB.ioWDDirID = dirID;
  147.     
  148.     if (err=PBOpenWD(&myPB,0))
  149.         return(0);
  150.     
  151.     return (myPB.ioVRefNum);
  152. }
  153.  
  154. /**********************************************************************
  155.  * DirIterate - iterate over the files in a directory.
  156.  **********************************************************************/
  157. short DirIterate(short vRef,long dirId,HFileInfo *hfi)
  158. {
  159.     hfi->ioFDirIndex++;
  160.     hfi->ioDirID = dirId;
  161.     hfi->ioVRefNum = vRef;
  162.     return(PBGetCatInfo(hfi,False));
  163. }
  164.  
  165. /**********************************************************************
  166.  * CopyFBytes - copy bytes from one file to another
  167.  **********************************************************************/
  168. int CopyFBytes(short fromRefN,long fromOffset,long length,short toRefN,long toOffset)
  169. {
  170.     int err;
  171.     UHandle buffer;
  172.     long size;
  173.     long count;
  174.     
  175.     if (err = SetFPos(fromRefN,fsFromStart,fromOffset)) return (err);
  176.     if (err = SetFPos(toRefN,fsFromStart,toOffset)) return (err);
  177.     
  178.     size = OPTIMAL_BUFFER > length ? length : OPTIMAL_BUFFER;
  179.     if ((buffer=NuHandle(size))==nil) return(MemError());
  180.     
  181.     LDRef(buffer);
  182.     do
  183.     {
  184.         count = size > length ? length : size;
  185.         if (err = FSRead(fromRefN,&count,*buffer)) break;
  186.         if (err = FSWrite(toRefN,&count,*buffer)) break;
  187.         length -= count;
  188.     }
  189.     while (length);
  190.     DisposHandle(buffer);
  191.     return(err);
  192. }
  193.  
  194. /************************************************************************
  195.  * MySFGetFile - get a file with SFGetFile
  196.  * Returns True if a file was got.
  197.  ************************************************************************/
  198. Boolean MySFGetFile(ProcPtr filter, long *dirIdPtr, short *vRefPtr, UPtr namePtr,short typeCount,SFTypeList tl)
  199. {
  200.     SFReply sfr;
  201.     Str255 junk;
  202.     Point where;
  203.     
  204.     StdFileSpot(&where,SFGETFILE_ID);
  205.     *junk = 0;
  206.     SFPGetFile(where,junk,filter,typeCount,tl,nil,&sfr,SFGETFILE_ID,DlgFilter);
  207.     
  208.     if (!sfr.good) return(False);
  209.     BlockMove(sfr.fName,namePtr,*sfr.fName+1);
  210.     *dirIdPtr = GetMyDirID(sfr.vRefNum);
  211.     *vRefPtr = sfr.vRefNum;
  212.     return(True);
  213. }
  214.  
  215. /************************************************************************
  216.  * StdFilespot - figure out where a stdfile dialog should go
  217.  ************************************************************************/
  218. void StdFileSpot(Point *where, short id)
  219. {
  220.     Rect r,in;
  221.     DialogTHndl dTempl;             
  222.     if ((dTempl=(DialogTHndl)GetResource('ALRT',id)) ||
  223.             (dTempl=(DialogTHndl)GetResource('DLOG',id)))
  224.     {
  225.         r = (*dTempl)->boundsRect;
  226.         in = qd.screenBits.bounds;
  227.         in.top += GetMBarHeight();
  228.         ThirdCenterRectIn(&r,&in);
  229.         where->h = r.left;
  230.         where->v = r.top;
  231.     }
  232.     else
  233.     {
  234.         where->h = 100;
  235.         where->v = 100;
  236.     }
  237. }
  238.  
  239. /************************************************************************
  240.  * FSHOpen - like FSOpen, but takes a dirId and permissions, too.
  241.  ************************************************************************/
  242. short FSHOpen(UPtr name,short vRefN,long dirId,short *refN,short perm)
  243. {
  244.     HIOParam pb;
  245.     int err;
  246.     Str255 newName;
  247.     
  248.     PCopy(newName,name);
  249.     if (err=MyResolveAlias(&vRefN,&dirId,newName,nil)) return(err);
  250.     pb.ioNamePtr = newName;
  251.     pb.ioVRefNum = vRefN;
  252.     pb.ioPermssn = perm;
  253.     pb.ioMisc = nil;
  254.     ((HFileParam *)&pb)->ioDirID = dirId;
  255.     err = PBHOpen(&pb,False);
  256.     if (!err) *refN = pb.ioRefNum;
  257.     return(err);
  258. }
  259.  
  260. /************************************************************************
  261.  * RFHOpen - like RFOpen, but with dirId and permissions
  262.  ************************************************************************/
  263. short RFHOpen(UPtr name,short vRefN,long dirId,short *refN,short perm)
  264. {
  265.     HIOParam pb;
  266.     int err;
  267.     
  268.     pb.ioCompletion = nil;
  269.     pb.ioNamePtr = name;
  270.     pb.ioVRefNum = vRefN;
  271.     pb.ioVersNum = 0;
  272.     pb.ioPermssn = perm;
  273.     pb.ioMisc = nil;
  274.     ((HFileParam *)&pb)->ioDirID = dirId;
  275.     err = PBHOpenRF(&pb,False);
  276.     if (!err) *refN = pb.ioRefNum;
  277.     return(err);
  278. }
  279.  
  280. /************************************************************************
  281.  * MyAllocate - do a PBAllocate call
  282.  ************************************************************************/
  283. int MyAllocate(short refN,long size)
  284. {
  285.     IOParam pb;
  286.     
  287.     pb.ioCompletion = nil;
  288.     pb.ioRefNum = refN;
  289.     pb.ioReqCount = size;
  290.     return(PBAllocate(&pb,False));
  291. }
  292.  
  293. /************************************************************************
  294.  * SFPutOpen - open a file for write, using stdfile
  295.  ************************************************************************/
  296. short SFPutOpen(UPtr name,short *vRef,long creator,long type,short *refN,ProcPtr filter,short id)
  297. {
  298.     Str255 scratch;
  299.     Point where;
  300.     SFReply sfr;
  301.     short err;
  302.     
  303.     /*
  304.      * do the standard file stuff
  305.      */
  306.     if (!MommyMommy(ATTENTION,nil)) return(1);
  307.     StdFileSpot(&where,SFPUTFILE_ID);
  308.     GetRString(scratch,SAVEAS_PROMPT);
  309.     SFPPutFile(where,scratch,name,nil,&sfr,id,filter);
  310.     if (!sfr.good) return(1);
  311.     BlockMove(sfr.fName,name,*sfr.fName+1);
  312.     *vRef = sfr.vRefNum;
  313.     
  314.     /*
  315.      * create && open the file
  316.      */
  317.     if (err=MakeResFile(name,*vRef,0,creator,type))
  318.     {
  319.         FileSystemError(COULDNT_SAVEAS,name,err);
  320.         return(err);
  321.     }
  322.  
  323.     if (err=FSOpen(name,*vRef,refN))
  324.     {
  325.         FileSystemError(COULDNT_SAVEAS,name,err);
  326.         FSDelete(name,*vRef);
  327.         return(err);
  328.     }
  329.  
  330.     if (err=SetEOF(*refN,0))
  331.     {
  332.         FileSystemError(COULDNT_SAVEAS,name,err);
  333.         FSDelete(name,*vRef);
  334.         return(err);
  335.     }
  336.     
  337.     return(noErr);
  338. }
  339.  
  340. /************************************************************************
  341.  * IsText - is a file of type TEXT or not?
  342.  ************************************************************************/
  343. Boolean IsText(UPtr volName,long dirId,UPtr name)
  344. {
  345.     FInfo info;
  346.     Str255 scratch;
  347.     short oldVol;
  348.     short newVol;
  349.     
  350.     GetVol(scratch,&oldVol);
  351.     SetVol(volName,0);
  352.     GetVol(scratch,&newVol);
  353.     
  354.     /*
  355.      * find the file, and get info
  356.      */
  357.     HGetFInfo(newVol,dirId,name,&info);
  358.     
  359.     SetVol(nil,oldVol);
  360.     return(info.fdType=='TEXT');    
  361. }
  362.  
  363. /************************************************************************
  364.  * SpinOn - spin until a return code is not inProgress or cacheFault
  365.  ************************************************************************/
  366. short SpinOn(short *rtnCodeAddr,long maxTicks)
  367. {
  368.     long ticks=TickCount();
  369.     long startTicks=ticks+120;
  370.     long now;
  371.     EventRecord event;
  372.     extern ConnHandle CnH;
  373.     Boolean oldCommandPeriod = CommandPeriod;
  374.     
  375.     CommandPeriod = False;
  376.     do
  377.     {
  378.         now = TickCount();
  379.         if (now>startTicks && now-ticks>10) {CyclePendulum();ticks=now;}
  380.         if (WNE(everyEvent,&event,0))
  381.         {
  382.             (void) MiniMainLoop(&event);
  383.             if (CommandPeriod) return(userCancelled);
  384.         }
  385.         if (!UseCTB) ClearICMP();
  386.         if (CnH) MyCMIdle();
  387.         if (maxTicks && startTicks+maxTicks < now+120) break;
  388.     }
  389.     while (*rtnCodeAddr == inProgress || *rtnCodeAddr == cacheFault);
  390.     CommandPeriod = oldCommandPeriod;
  391.     return(*rtnCodeAddr);
  392. }
  393.  
  394. /************************************************************************
  395.  * IsItAFolder - is the specified file a folder?
  396.  ************************************************************************/
  397. Boolean IsItAFolder(short vRef,long inDirId,UPtr name)
  398. {
  399.     HFileInfo hfi;
  400.     short err;
  401.     
  402.     hfi.ioCompletion=nil;
  403.     hfi.ioNamePtr=name;
  404.     hfi.ioVRefNum=vRef;
  405.     hfi.ioDirID=inDirId;
  406.     hfi.ioFDirIndex=0;
  407.     if (err=PBGetCatInfo(&hfi,False)) return(err);
  408.     return(0!=(hfi.ioFlAttrib&0x10));
  409. }
  410.  
  411. /************************************************************************
  412.  * FolderFileCount - count the files in a folder
  413.  ************************************************************************/
  414. short FolderFileCount(long inDirId,UPtr name)
  415. {
  416.     HFileInfo hfi;
  417.     short err;
  418.     
  419.     FILL(hfi,name,MyVRef,inDirId);
  420.     hfi.ioFDirIndex=0;
  421.     if (err=PBGetCatInfo(&hfi,False)) return(-1);
  422.     return(hfi.ioFlStBlk);
  423. }
  424.  
  425. /************************************************************************
  426.  * Move - Move a file or directory
  427.  ************************************************************************/
  428. short HMove(short vRef,long fromDirId,UPtr fromName,long toDirId,UPtr toName)
  429. {
  430.     CMovePBRec pb;
  431.     
  432.     pb.ioNamePtr = fromName;
  433.     pb.ioDirID = fromDirId;
  434.     pb.ioNewDirID = toDirId;
  435.     pb.ioNewName = toName;
  436.     pb.ioVRefNum = vRef;
  437.                 
  438.     return(PBCatMove(&pb,False));
  439. }
  440.  
  441.  
  442. short HGetFileInfo(short vRef,long dirId,UPtr name,HFileInfo *hfi)
  443. {
  444.     short err;
  445.     Str255 newName;
  446.     
  447.     PCopy(newName,name);
  448.     if (err=MyResolveAlias(&vRef,&dirId,newName,nil)) return(err);
  449.     WriteZero(hfi,sizeof(*hfi));
  450.     FILL(*hfi,newName,vRef,dirId);
  451.     return(PBHGetFInfo(hfi,False));
  452. }
  453.  
  454. short HSetFileInfo(short vRef,long dirId,UPtr name,HFileInfo *hfi)
  455. {
  456.     short err;
  457.     Str255 newName;
  458.     
  459.     PCopy(newName,name);
  460.     if (err=MyResolveAlias(&vRef,&dirId,newName,nil)) return(err);
  461.     FILL(*hfi,newName,vRef,dirId);
  462.                 return(PBHSetFInfo(hfi,False));
  463. }
  464.  
  465. /************************************************************************
  466.  * I am indebted to Tim Maroney (tim@toad.com) for the following routines.
  467.  ************************************************************************/
  468. static Boolean good, noSys, needWrite, allowFloppy, allowDesktop;
  469. static SFReply reply;
  470.  
  471. pascal Boolean
  472. FolderFilter(FileParam *pb);
  473. pascal Boolean
  474. FolderFilter(FileParam *pb)
  475. {
  476. #pragma unused(pb)
  477.                 return true;
  478. }
  479.  
  480. pascal short
  481. FolderItems(short item,DialogPtr dlog);
  482. pascal short
  483. FolderItems(short item,DialogPtr dlog)
  484. {
  485. #pragma unused(dlog)
  486.                 if (item == 2) {
  487.                                 good = true;
  488.                                 item = 3;
  489.                 }
  490.                 return item;
  491. }
  492.  
  493. pascal void
  494. FolderEvents(DialogPtr dialog, EventRecord *event,short *item);
  495. pascal void
  496. FolderEvents(DialogPtr dialog, EventRecord *event,short *item)
  497. {
  498. #pragma unused(event,item)
  499.                 ControlHandle ch;
  500.                 short type;
  501.                 Rect r;
  502.                 HVolumeParam vp;
  503.                 
  504.                 /* disable if a directory is selected in the list */
  505.                 GetDItem(dialog, 2, &type, &ch, &r);
  506. #ifdef BOY_DO_PEOPLE_HATE_THIS    /* scd, 2/15/90 */
  507.                 if (reply.fType) {
  508.                                 HiliteControl(ch, 255);
  509.                                 return;
  510.                 }
  511. #endif
  512.  
  513.                 /* get information on the volume */
  514.                 vp.ioNamePtr = (StringPtr)0;
  515.                 vp.ioVRefNum = -*(short *)SFSaveDisk;
  516.                 vp.ioVolIndex = 0;
  517.                 if (PBHGetVInfo(&vp, false))
  518.                                 HiliteControl(ch, 255);
  519.                 else if (vp.ioVSigWord != 0x4244)                                     /* HFS? */
  520.                                 HiliteControl(ch, 255);
  521.                 else if (vp.ioVDRefNum >= 0 || vp.ioVDrvInfo == 0)    /* ejected? */
  522.                                 HiliteControl(ch, 255);
  523.                 else if (needWrite && (vp.ioVAtrb & 0x8080))                /* locked? */
  524.                                 HiliteControl(ch, 255);
  525.                 else if (!allowFloppy && vp.ioVDRefNum == -5)             /* floppy? */
  526.                                 HiliteControl(ch, 255);
  527.                 else if (!allowDesktop && *(long *)CurDirStore == 2)                /* desktop? */
  528.                                 HiliteControl(ch, 255);
  529.                 else if (noSys && *(long *)CurDirStore == vp.ioVFndrInfo[0]) /* blessed? */
  530.                                 HiliteControl(ch, 255);
  531.                 else        HiliteControl(ch, 0);
  532. }
  533.  
  534. Boolean
  535. GetFolder(char *name,short *volume,long *folder,Boolean writeable,Boolean system,Boolean floppy,Boolean desktop)
  536. {
  537.                 short oldvol = -*(short *)SFSaveDisk;
  538.                 long oldfolder = *(long *)CurDirStore;
  539.                 Point where;
  540.                 StdFileSpot(&where,GETFOLDER_DLOG); /* sd 1/91 */
  541.                 good = false;
  542.                 if (*volume && *folder) {
  543.                                 *(short *)SFSaveDisk = -*volume;
  544.                                 *(long *)CurDirStore = *folder;
  545.                 }
  546.                 needWrite = writeable, noSys = !system, allowFloppy = floppy;
  547.                 allowDesktop = desktop;
  548.                 SFPGetFile(where, (char *)0, FolderFilter, -1, 0, FolderItems,
  549.                                      &reply, GETFOLDER_DLOG, FolderEvents);
  550.                 if (!good) return false;
  551.                 *volume = -*(short *)SFSaveDisk;
  552.                 *folder = *(long *)CurDirStore;
  553.                 /*FullFileName(name, "", -*(short *)SFSaveDisk, *(long *)CurDirStore); sd 1/91 */
  554.                 GetMyVolName(*volume,name);         /* sd 1/91 */
  555.                 *(short *)SFSaveDisk = -oldvol;
  556.                 *(long *)CurDirStore = oldfolder;
  557.                 return true;
  558. }
  559.  
  560. /************************************************************************
  561.  * CopyRFork - copy the resource fork from one file to another
  562.  ************************************************************************/
  563. short CopyRFork(short vRef,long dirId,UPtr name,short fromVRef,
  564.                                 short fromDirId,Uptr fromName)
  565. {
  566.     short err;
  567.     short fromRef,toRef;
  568.     UPtr buffer=NuPtr(OPTIMAL_BUFFER);
  569.     long bSize=OPTIMAL_BUFFER;
  570.     long eof=0;
  571.     
  572.     if (!buffer) return(MemError());
  573.     if (!(err=RFHOpen(fromName,fromVRef,fromDirId,&fromRef,fsRdPerm)))
  574.     {
  575.         if (!(err=RFHOpen(name,vRef,dirId,&toRef,fsRdWrPerm)))
  576.         {
  577.             GetEOF(fromRef,&eof);
  578.             for (bSize=MIN(OPTIMAL_BUFFER,eof);!err&&eof;bSize=(OPTIMAL_BUFFER,eof))
  579.             {
  580.                 if (!(err=FSRead(fromRef,&bSize,buffer)))
  581.                 {
  582.                     eof -= bSize;
  583.                     err = FSWrite(toRef,&bSize,buffer);
  584.                 }
  585.             }
  586.             FSClose(toRef);
  587.         }
  588.         FSClose(fromRef);
  589.     }
  590.     DisposPtr(buffer);
  591.     return(err);    
  592. }
  593.  
  594. /************************************************************************
  595.  * CopyFInfo - copy the file info from one file to another
  596.  ************************************************************************/
  597. short CopyFInfo(short vRef,long dirId,UPtr name,short fromVRef,
  598.                                 short fromDirId,Uptr fromName)
  599. {
  600.     short err;
  601.     FInfo info;
  602.     if (!(err=HGetFInfo(fromVRef,fromDirId,fromName,&info)))
  603.         err=HSetFInfo(vRef,dirId,name,&info);
  604.     return(err);
  605. }
  606.  
  607. /************************************************************************
  608.  * MyResolveAlias - resolve an alias
  609.  ************************************************************************/
  610. short MyResolveAlias(short *vRef,long *dirId,UPtr name,Boolean *wasAlias)
  611. {
  612.     FSSpec theSpec;
  613.     Boolean folder;
  614.     long haveAlias;
  615.     short err=noErr;
  616.     Boolean wasIt;
  617.     
  618.     if (wasAlias) *wasAlias = False;
  619.     if (!Gestalt(gestaltAliasMgrAttr,&haveAlias) && haveAlias&0x1)
  620.     {
  621.         if (!(err=FSMakeFSSpec(*vRef,*dirId,name,&theSpec)) &&
  622.                 !(err=ResolveAliasFile(&theSpec,True,&folder,&wasIt)))
  623.         {
  624.             if (wasIt)
  625.             {
  626.                 *vRef = theSpec.vRefNum;
  627.                 *dirId = theSpec.parID;
  628.                 PCopy(name,theSpec.name);
  629.                 name[*name+1] = 0;
  630.                 if (wasAlias) *wasAlias = True;
  631.             }
  632.         }
  633.     }
  634.     return(err);
  635. }
  636.  
  637. /************************************************************************
  638.  * FSWriteP - write a Pascal string
  639.  ************************************************************************/
  640. short FSWriteP(short refN,UPtr pString)
  641. {
  642.     long count = *pString;
  643.     return(FSWrite(refN,&count,pString+1));
  644. }
  645.  
  646.  
  647. /************************************************************************
  648.  * GetFileByRef - figure out the name & vol of a file from an open file
  649.  ************************************************************************/
  650. short GetFileByRef(short refN,short *vRef,long *dirId,UPtr name)
  651. {
  652.     FCBPBRec fcb;
  653.     short err;
  654.     
  655.     fcb.ioCompletion = nil;
  656.     fcb.ioVRefNum = nil;
  657.     fcb.ioRefNum = refN;
  658.     fcb.ioFCBIndx = 0;
  659.     fcb.ioNamePtr = name;
  660.     if (err=PBGetFCBInfo(&fcb,False)) return(err);
  661.     
  662.     *vRef = fcb.ioFCBVRefNum;
  663.     *dirId = fcb.ioFCBParID;
  664.     return(noErr);
  665. }
  666.  
  667. /************************************************************************
  668.  * VolumeFree - return the free space on a volume
  669.  ************************************************************************/
  670. long VolumeFree(short vRef)
  671. {
  672.     Str31 name;
  673.     long vFree;
  674.     
  675.     *name = 0;
  676.     if (GetVInfo(0,name,&vRef,&vFree)) return(0);
  677.     return(vFree);
  678. }
  679.  
  680. /************************************************************************
  681.  * FSTabWrite - write, expanding tabs
  682.  ************************************************************************/
  683. short FSTabWrite(short refN,long *count,UPtr buf)
  684. {
  685.     UPtr p;
  686.     UPtr end = buf+*count;
  687.     long written=0;
  688.     short err=noErr;
  689.     long writing;
  690.     static short charsOnLine=0;
  691.     UPtr nl;
  692.     short stops=0;
  693.     
  694.     if (!FakeTabs) return(FSZWrite(refN,count,buf));
  695.     for (p=buf;p<end;p=buf=p+1)
  696.     {
  697.         nl = buf-charsOnLine-1;
  698.         while (p<end && *p!=tabChar)
  699.         {
  700.             if (*p=='\n') nl=p;
  701.             p++;
  702.         }
  703.         writing = p-buf;
  704.         err=FSZWrite(refN,&writing,buf);
  705.         written += writing;
  706.         if (err) break;
  707.         charsOnLine = p-nl-1;
  708.         if (p<end)
  709.         {
  710.             if (!stops) stops = GetRLong(TAB_DISTANCE);
  711.             writing = stops-(charsOnLine)%stops;
  712.             charsOnLine = 0;
  713.             err = FSZWrite(refN,&writing,"              ");
  714.             written += writing;
  715.             if (err) break;
  716.         }
  717.     }
  718.     *count = written;
  719.     return(err);
  720. }